package com.example.im.ws;

import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @ClassName WebSocketServer
 * @Description 处理websocket 连接
 * @Author guchuanhang
 * @date 2025/1/25 14:01
 * @Version 1.0
 **/

@Slf4j
public class WebSocketServer extends TextWebSocketHandler {

    private final Object syncObject = new Object();
    private final List<SessionData> sessions =
            new ArrayList<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("New connection established: " + session.getId());
        SessionData sessionData = new SessionData(session);
        synchronized (syncObject) {
            sessions.add(sessionData);
        }
        MsgData msgData = new MsgData();
        msgData.setType(MsgType.SELF);
        msgData.setSessionId(session.getId());
        session.sendMessage(new TextMessage(new Gson().toJson(msgData)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message)
            throws Exception {
        String payload = message.getPayload();
        log.info("handleTextMessage: " + session.getId());
        log.info("Received message: " + payload);
        final MsgData msgData = new Gson().fromJson(payload, MsgData.class);
        //master 发来的需求.
        switch (msgData.getType()) {

            case MsgType.HEART_BEAT: {
                //heart beat
                break;
            }

            case MsgType.REQ: {
                //set master
                {
                    SessionData sessionData = null;
                    synchronized (syncObject) {
                        final Optional<SessionData> any = sessions.stream().
                                filter(s -> s.getSessionId()
                                        .equals(session.getId())).findAny();
                        if (any.isPresent()) {
                            sessionData = any.get();
                        }
                    }
                    if (null != sessionData) {
                        //set master.
                        sessionData.setSessionType(ClientType.MASTER);
                        sessionData.setFileId(msgData.getFileId());
                    }
                }
                //set slave
                {

                    SessionData sessionData = null;
                    synchronized (syncObject) {
                        final Optional<SessionData> any = sessions.stream().
                                filter(s -> s.getSessionId().equals(msgData.getSessionId())).findAny();
                        if (any.isPresent()) {
                            sessionData = any.get();
                        }
                    }

                    if (null != sessionData) {
                        sessionData.setSessionType(ClientType.SALVER);
                        sessionData.setFileId(msgData.getFileId());

                        MsgData msgData1 = new MsgData();
                        msgData1.setType(MsgType.REQ);
                        msgData1.setFileId(msgData.getFileId());
                        sessionData.getSession().sendMessage(new TextMessage(new Gson().toJson(msgData1)));
                    } else {
                        //pc session error.
                        MsgData msgData1 = new MsgData();
                        msgData1.setType(MsgType.ERR_SESSION);
                        session.sendMessage(new TextMessage(new Gson().toJson(msgData1)));
                    }
                }
                break;
            }
            case MsgType.UPDATE: {
                //slf

                SessionData sessionData = null;
                synchronized (syncObject) {
                    final Optional<SessionData> any = sessions.stream().
                            filter(s -> s.getSessionId().equals(session.getId())).findAny();
                    if (any.isPresent()) {
                        sessionData = any.get();
                    }
                }
                if (null != sessionData) {
                    final String fileId = sessionData.getFileId();
                    List<SessionData> collect;
                    synchronized (syncObject) {
                        collect =
                                sessions.stream().filter(s -> (null != s.getFileId() && s.getFileId().
                                        equals(fileId)) || (null == s.getSession() || !s.getSession().isOpen())).collect(Collectors.toList());
                    }
                    if (collect.isEmpty()) {
                        return;
                    }
                    List<SessionData> errList = new ArrayList<>();
                    for (SessionData s : collect) {
                        if (null == s.getSession() || !s.getSession().isOpen()) {
                            errList.add(s);
                            continue;
                        }
                        //不需要给自己发送了
                        if (s.getSessionId().equals(session.getId())) {
                            continue;
                        }

                        MsgData msgData1 = new MsgData();
                        msgData1.setType(MsgType.UPDATE);
                        try {
                            s.getSession().sendMessage(new TextMessage(new Gson().toJson(msgData1)));
                        } catch (Exception e) {
                            e.printStackTrace();
                            errList.add(s);
                        }
                    }
                    synchronized (syncObject) {
                        sessions.removeAll(errList);
                    }

                }
                break;
            }
        }

    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        log.info("Connection closed: " + session.getId() + ", Status: " + status);
        SessionData sessionData = null;
        synchronized (syncObject) {
            Optional<SessionData> any = sessions.stream().
                    filter(s -> s.getSessionId().equals(session.getId())).findAny();
            if (any.isPresent()) {
                sessionData = any.get();
            }
        }
        if (null == sessionData) {
            return;
        }
        final String fileId = sessionData.getFileId();
        //slave just ignore and delete.
        if (ClientType.SALVER == sessionData.getSessionType()) {
            sessions.remove(sessionData);
            return;
        }
        if (ClientType.MASTER == sessionData.getSessionType()) {
            List<SessionData> collect;
            synchronized (syncObject) {
                collect =
                        sessions.stream().filter(s ->
                                (null != s.getFileId()
                                        && s.getFileId().equals(fileId)) ||
                                        (null == s.getSession() || !s.getSession().isOpen())).collect(Collectors.toList());
            }
            if (collect.isEmpty()) {
                return;
            }
            for (SessionData s : collect) {
                final WebSocketSession session1 = s.getSession();
                if (null == session1 || !session1.isOpen()) {
                    continue;
                }
                session1.close();
            }
            synchronized (syncObject) {
                sessions.removeAll(collect);
            }
        }
    }
}
